use crate::config::{self, ConfigNode};
use actix_web::{
    guard::{self, Guard},
    web::Query,
};
use awc::error::HeaderValue;
use ipnetwork::IpNetwork;
use serde::Deserialize;
use std::collections::HashMap;
use std::{
    net::{IpAddr, Ipv4Addr, Ipv6Addr},
    str::FromStr,
};

pub const HEADER_APP_KEY: &str = "AYISHA_APP_KEY";
pub const HEADER_APP_SECRET: &str = "AYISHA_APP_SECRET";

// pub const KEY_LOCAL_HOST: &str = "AYISHA_LOCAL_HOST";
pub const HEADER_KEY_MEMBERS: &str = "AYISHA_MEMBERS";

pub struct IMGuard;
pub struct ClusterModeGuard;

#[derive(Deserialize, Debug)]
pub struct User {
    pub app_key: String,
    pub app_secret: String,
    pub ip: String,
}

pub struct IMAuth;

fn is_valid_member(_rhost: &str, header_members: &HeaderValue) -> bool {
    let members = match header_members.to_str() {
        Ok(s) => s,
        Err(_) => return false,
    };

    let r_members = match serde_json::from_str::<Vec<ConfigNode>>(members) {
        Ok(m) => m,
        Err(e) => {
            log::error!("JSON parse failed, cause: {e}");
            return false;
        }
    };

    // 比较本机的member与客户端的members
    // 全部要一致，否则就报错：配置不一致
    //
    let l_members = config::get_members();
    for rm in r_members.iter() {
        for lm in l_members.iter() {
            if rm.id != lm.id {
                continue;
            }
            let r_host = rm.host.to_owned();
            // if rm.host == Some("127.0.0.1".to_owned()) {
            //     r_host = Some(rhost.to_owned());
            // }

            if lm.host != r_host {
                log::error!("Members Configuration Error with id {}, Member host [{}] mismatch with Clients [{}]", lm.id, lm.host.clone().unwrap_or_default(), r_host.unwrap_or_default());
                return false;
            }
        }
    }
    true
}

impl Guard for IMGuard {
    fn check(&self, ctx: &guard::GuardContext<'_>) -> bool {
        let client_ip = ctx.head().peer_addr.unwrap().ip().to_string();
        let header_members = ctx.head().headers().get(HEADER_KEY_MEMBERS);
        if let Some(header_members) = header_members {
            if !is_valid_member(&client_ip, header_members) {
                return false;
            }
        }

        // 如果参数有值，优先使用参数的值
        // 如果参数没有值，则获取header的值，header无值则无权限

        // 通过参数获取
        if let Some(query_str) = ctx.head().uri.query() {
            if let Ok(params) = Query::<HashMap<String, String>>::from_query(query_str) {
                // 包含大小写
                let mut param_app_key: Option<&String> = None;
                let mut param_app_secret: Option<&String> = None;
                for key in params.keys() {
                    if key.eq_ignore_ascii_case(HEADER_APP_KEY) {
                        param_app_key = params.get(key);
                    } else if key.eq_ignore_ascii_case(HEADER_APP_KEY) {
                        param_app_secret = params.get(key);
                    }
                }

                let mut valid = false;
                if let Some(app_key) = param_app_key {
                    // 参数存在，且参数为空
                    valid = valid | !app_key.is_empty();
                }

                if let Some(app_secret) = param_app_secret {
                    valid = valid & !app_secret.is_empty();
                }

                if valid {
                    return IMAuth::new().authenticate(&User {
                        app_key: param_app_key.unwrap().to_owned(),
                        app_secret: param_app_secret.unwrap().to_owned(),
                        ip: client_ip,
                    });
                }
            }
        }

        let header_app_key = ctx.head().headers().get(HEADER_APP_KEY);
        let header_app_secret = ctx.head().headers().get(HEADER_APP_SECRET);
        if header_app_key.is_none() || header_app_secret.is_none() {
            // header无参数
            return false;
        }

        let mut valid = false;
        // 通过header获取
        if let Some(app_key) = header_app_key {
            valid = valid | !app_key.is_empty();
        }
        if let Some(app_secret) = header_app_secret {
            valid = valid & !app_secret.is_empty();
        }

        if valid {
            return IMAuth::new().authenticate(&User {
                app_key: header_app_key.unwrap().to_str().unwrap().to_owned(),
                app_secret: header_app_secret.unwrap().to_str().unwrap().to_owned(),
                ip: client_ip,
            });
        }

        false
    }
}

impl IMAuth {
    pub fn new() -> Self {
        IMAuth {}
    }

    // 检查权限
    // 考虑添加跳过鉴权表功能
    pub fn authenticate(&self, user: &User) -> bool {
        let ip: IpAddr = match Ipv4Addr::from_str(&user.ip) {
            Ok(ip) => IpAddr::V4(ip),
            Err(_) => match Ipv6Addr::from_str(&user.ip) {
                Ok(ipv6) => IpAddr::V6(ipv6),
                Err(_) => {
                    log::error!("- - - Invalid client address: {}", &user.ip);
                    return false;
                }
            },
        };

        let idents = config::HostBasedAuth::new().match_ident(&user.app_key, &user.app_secret);

        let mut valid_user = false;
        let mut valid_addr = false;

        for (host, _ident) in idents {
            valid_user = true;
            // 判断是否设置了白名单
            // 考虑IPv4和IPv6
            // let host = userauth.host;
            // 有效
            // 未指定，则全部 IP 放通
            valid_addr |= host.is_empty();
            if valid_addr {
                break;
            }

            // 是否匹配
            valid_addr |= match IpNetwork::from_str(&host) {
                Ok(net) => {
                    // 包含，放通
                    // 未指定，则全部 IP 放通
                    // 双方都是回环地址，则放通
                    let mut is_unspecified =
                        ip.is_ipv4() & net.is_ipv4() & net.network().is_unspecified();
                    is_unspecified |= ip.is_ipv6() & net.is_ipv6() & net.network().is_unspecified();

                    let mut is_loopback =
                        ip.is_ipv4() & net.is_ipv4() & net.network().is_loopback();
                    is_loopback |= ip.is_ipv6() & net.is_ipv6() & net.network().is_loopback();

                    log::debug!("[{}] - [{}] - hba_ident: Unspecified: {}, Address Contains IP: {}, Address & IP is Loopback: {}", ip, net, is_unspecified, net.contains(ip), is_loopback, );

                    is_unspecified | net.contains(ip) | is_loopback
                }
                Err(e) => {
                    log::error!("[{}] - - invalid hba_ident.homl address, cause: {}", ip, e);
                    false
                }
            };

            if valid_addr {
                break;
            }
        }

        if !valid_user {
            log::warn!("{} - - Access denied for APP_KEY or APP_SECRET", user.ip);
            return false;
        }

        if !valid_addr {
            log::warn!("{} - - Access denied for ADDRESS", user.ip);
            return false;
        }

        true
    }
}

impl Guard for ClusterModeGuard {
    fn check(&self, _ctx: &guard::GuardContext<'_>) -> bool {
        !config::get_server_singleton()
    }
}
